local skynet = require "skynet"
local mc = require "multicast"
local packdeal = require "packdeal"
local queue = require "skynet.queue"
local json = require "cjson"
json.encode_sparse_array(true)
require "config_redis"

local NetCmd = require "common/NetCmd"
local Common = require "common/Common"
local ErrNo = require "common/ErrNo"

UID_LOGIN = 1
ACCOUNT_LOGIN = 2
NEED_AUTH = true

local send_package = packdeal.send_package
local unpack_package = packdeal.unpack_package

CMD = {}
POST = {}
local SOCKET = {}
local REQUEST = {}
local gate
local agent = {}    -- 1 agent_address, 2 account, 3 uid
local account_to_agent = {}
local uid_to_agent = {}
local queue_dict = {}
local client_ip_arr = {}
local time_out_stop = {}

-- 每个账户对应一个队列，防止重入
function make_queue(fd, data, func)
    local account = fd
    if not queue_dict[account] then
        queue_dict[account] = queue()
    end
    local cs = queue_dict[account]
    local function closure()
        func(fd, data)
    end
    cs(closure)
end

-- 用户登陆
function REQUEST.login(fd, data)
    skynet.error("-- watchDog -- REQUEST.login -------> fd, data:", fd, json.encode(data))
    local ok = false
    local uid = 0
    local account = nil
    local cmd_rs = NetCmd.S2C_LOGIN_ACCOUNT
    local client_ip = ''
    if data.ip ~= nil then
        client_ip_arr[fd] = data.ip
        client_ip = data.ip
    end

    if data.md == nil then
        send_package(fd, { cmd = cmd_rs, ok = ok, msg = '登录失效,请重新登录!', code = ErrNo.UNKNOW_ERROR })
        return
    end

    --普通用户登陆
    if data.md ~= nil and data.admin_uid == nil then
        local unionid =  string.sub(data.md, 1, -5)
        local query_sql = string.format("SELECT uid FROM `user` WHERE `unionid`='%s'", unionid)
        local user_info = skynet.call("MG_SAVE", "lua", "update_sql",query_sql)
        if user_info ~= nil and user_info[1] ~= nil then
            uid = user_info[1]['uid']
        else
            send_package(fd, { cmd = cmd_rs, ok = ok, msg = "登录失效,帐号不存在!", code = ErrNo.UNKNOW_ERROR })
            return
        end
    end

    --管理员登陆
    if data.uid ~= nil and data.admin_uid ~= nil  then
        data.uid = math.ceil(tonumber(data.uid))
        account = uid
        uid = data.uid
    end

    if uid == 0 then
        send_package(fd, { cmd = cmd_rs, ok = ok, msg = '登录失效,请重新登录!', code = ErrNo.UNKNOW_ERROR })
        return
    end

    local user_info = skynet.call("MG_SAVE", "lua", "get_user_info", uid)
    if user_info == 0 then
        send_package(fd, { cmd = cmd_rs, ok = ok, msg = '登录失效,帐号不存在!', code = ErrNo.UNKNOW_ERROR })
        return
    end
    if user_info['status'] == 1 then
        send_package(fd, { cmd = cmd_rs, ok = ok, msg = '您的账户封号状态', code = ErrNo.FENGHAO })
        return
    end
    --cai  白名单
    -- local game_uid = {}
    -- game_uid.a749982 = 1
    -- game_uid.a100001 = 1
    -- game_uid.a440614 = 1
    -- game_uid.a706076 = 1
    -- if game_uid['a'..uid] == nil then
    -- 	msg = "游戏维护中"
    -- 	send_package(fd, {cmd=cmd_rs, ok=ok, msg=msg})
    -- 	return
    -- end
    local client_agent = uid_to_agent[uid] and uid_to_agent[uid].agent or nil
    ok = true
    local server_time = os.time()
    send_package(fd, { cmd = cmd_rs, code = 0, ok = ok, uid = uid, msg = '登陆成功', account = account, server_time = server_time })
 
    if account == nil then
        account = uid
    end  
    local update_info = {  ip = client_ip } 
    update_info.last_time = os.date("%Y-%m-%d %H:%M:%S")
    skynet.call("MG_SAVE", "lua", "set_user", uid, nil, nil, update_info)
    if data.room_id == nil then
        data.room_id = 0
    else
        data.room_id = math.floor(tonumber(data.room_id))
    end
    if data.room_id < 0 then
        data.room_id = 0
    end
    -- agent 登录
    if not client_agent then
        -- 创建agent
        client_agent = skynet.newservice("agent")
        agent[fd] = { client_agent, account, uid }
        skynet.call(client_agent, "lua", "start", gate, fd, account, uid, data.room_id, data)
        local now_client = 0
        for i, v in pairs(agent) do
            now_client = now_client + 1
        end
    else
        -- 判断是否为同一个fd 
        local tmp_fd = uid_to_agent[uid].fd
        if tmp_fd ~= fd then
            local addr = uid_to_agent[uid].addr
            local a = agent[tmp_fd]
            if a then
                skynet.send(a[1], "lua", "close")
                client_ip_arr[tmp_fd] = nil
                agent[tmp_fd] = nil
            end
            skynet.call(gate, "lua", "kick", tmp_fd)
        end
        print('---------client_agent-----------',client_agent)
        skynet.send(client_agent, "lua", "close", true)
        if uid_to_agent[uid] and uid_to_agent[uid].fd then
            agent[uid_to_agent[uid].fd] = nil
            client_ip_arr[uid_to_agent[uid].fd] = nil
        end

        client_agent = skynet.newservice("agent")
        agent[fd] = { client_agent, account, uid }
        skynet.call(client_agent, "lua", "start", gate, fd, account, uid, data.room_id)
    end
    time_out_stop[fd] = nil

    account_to_agent[account] = client_agent
    uid_to_agent[uid] = { agent = client_agent, fd = fd, addr = gate }

end

function POST.heartbeat(uid)
    if uid and uid_to_agent[uid] then
        send_package(uid_to_agent[uid].fd, { cmd = NetCmd.S2C_HEARTBEAT, uid = uid })
    end
end



function REQUEST.web_error(fd, data)
    local text = json.encode(data)
    packdeal.writelog_error('error', text)
end




function REQUEST.return_now_client_number(fd, data)
    local rs = {}
    local num = 0
    for i, v in pairs(agent) do
        num = num + 1
    end
    rs.now_num = num
    rs.cmd = NetCmd.S2C_RETURN_NOW_CLIENT_NUMBER
    send_package(fd, rs)
end

function REQUEST.close_room(fd, data)
    local room_id = math.ceil(data.room_id)
    local hall_name = skynet.call("MG_SAVE", "lua", "hall_name_hget", room_id)
    if hall_name == nil or hall_name == false then
        hall_name = 'NIUNIU_HALL'
    end
    skynet.call(hall_name, "lua", "close_room", room_id)
end

function REQUEST.c2s_sos(fd, data)
    if data.race ~= nil and data.room_id ~= nil and data.room_id > 0 then
        skynet.call("MG_SAVE", "lua", "del_race", data.race, data.room_id)
    end
    if data.uid ~= nil and data.room_id ~= nil and data.room_id > 0 then
        skynet.call("MG_SAVE", "lua", "jishao_race", data.room_id, data.uid)
    end
end

REQUEST.func_map = {
    [NetCmd.C2S_LOGIN_ACCOUNT]                    = REQUEST.login,
    [NetCmd.C2S_LOGIN]                            = REQUEST.login,
    [NetCmd.C2S_SOS]                            = REQUEST.c2s_sos,
    [NetCmd.C2S_RETURN_NOW_CLIENT_NUMBER]        = REQUEST.return_now_client_number,
}

function SOCKET.open(fd, addr)
    local index = string.find(addr, ":")
    local ip = string.sub(addr, 1, index - 1)
    client_ip_arr[fd] = ip
    skynet.call(gate, "lua", "accept", fd)

    local timer_time_out_stop = function()       -- 定时器触发 
        if time_out_stop[fd] then
            skynet.call(gate, "lua", "kick", fd)
        end
    end
    time_out_stop[fd] = add_skynet_timer(30, timer_time_out_stop)
end

-- 通知agent连接断开
local function close_agent(fd)
    local ok = false
    local a = agent[fd]
    if a then
        skynet.send(a[1], "lua", "close")
        client_ip_arr[fd] = nil
        agent[fd] = nil
    end
    skynet.call(gate, "lua", "kick", fd)
end

function SOCKET.close(fd)
    --skynet.error("socket close",fd,client_ip_arr[fd])
    close_agent(fd)
end

function SOCKET.error(fd, msg)
    --skynet.error("socket error",fd, msg)
    close_agent(fd)
end

function SOCKET.warning(fd, size)
end

function SOCKET.data(fd, msg)
    if msg == nil or string.len(msg) < 5 then
        return
    end
    local json_data = json.decode(msg)
    if not json_data then
        return
    end
    if json_data.cmd ~= 5 then
        skynet.error("-- watchDog -- SOCKET.data -------> msg:", msg)
    end
    local f = REQUEST.func_map[json_data.cmd]
    if not f then
     --   print('===========62=============', msg)
        send_package(fd, { cmd = NetCmd.S2C_VERSION, uid = 0 })
        return
    end
    if json_data.cmd == NetCmd.C2S_SOS or json_data.cmd == NetCmd.C2S_BROAD or json_data.cmd == NetCmd.C2S_LOGIN or json_data.cmd == NetCmd.C2S_LOGIN_ACCOUNT or json_data.cmd == NetCmd.C2S_RETURN_NOW_CLIENT_NUMBER then
        f(fd, json_data)
    else
        make_queue(fd, json_data, f)
    end
end


function CMD.start(conf)
    skynet.call(gate, "lua", "open", conf)
end

function CMD.close(fd)
    close_agent(fd)
end

function CMD.close_agent(fd)
    close_agent(fd)
end

function POST.destory(account, uid)
    -- skynet.error("watchlog destory",account,uid)
    if account_to_agent[account] ~= nil then
        account_to_agent[account] = nil
    end
    if uid_to_agent[uid] ~= nil then
        uid_to_agent[uid] = nil
    end
end

skynet.start(function()
    skynet.dispatch("lua", function(session, source, cmd, subcmd, ...)
        if cmd == "socket" then
            local f = SOCKET[subcmd]
            f(...)
        else
            local f = CMD[cmd]
            if f then
                skynet.ret(skynet.pack(f(subcmd, ...)))
            else
                f = POST[cmd]
                if f then
                    f(subcmd, ...)
                end
            end
        end
    end)

    gate = skynet.newservice("wsgate")
end)